﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using Microsoft.SharePoint;
using Trentacular.SharePoint.Deployment.Model;
using System.Collections;

namespace Trentacular.SharePoint.Deployment.Views
{
    public class SolutionsExplorer : BaseView
    {
        public SolutionsExplorer(FrameworkElement owner) : base(owner) { }

        private List<RootContainer> _root;
        public List<RootContainer> Root
        {
            get
            {
                if (_root == null)
                {
                    _root = new List<RootContainer>(1);
                    _root.Add(new RootContainer { View = this });
                }
                return _root;
            }
        }

        public class RootContainer : SolutionsViewComponent
        {
            public override TreeViewLevels Level { get { return TreeViewLevels.Root; } }

            public override string DetailHeaderText { get { return "Installed Solutions"; } }

            private ObservableCollection<SolutionContainer> _solutions;
            public ObservableCollection<SolutionContainer> Solutions
            {
                get
                {
                    if (_solutions == null)
                    {
                        _solutions = new ObservableCollection<SolutionContainer>();
                        FillCollectionAsync(_solutions, delegate()
                        {
                            return Data.Farm.Solutions
                                .OrderBy(s => s.DisplayName)
                                .Select(s => new SolutionContainer
                                {
                                    Parent = this,
                                    Solution = s
                                });
                        });
                    }
                    return _solutions;
                }
            }
        }

        public class SolutionContainer : SolutionsViewComponent<RootContainer>
        {
            public override TreeViewLevels Level { get { return TreeViewLevels.Solution; } }

            public override string DetailHeaderText { get { return "Solution Contents"; } }

            public Solution Solution { get; set; }

            private ObservableCollection<FeatureDefinitionContainer> _featureDefinitions;
            public ObservableCollection<FeatureDefinitionContainer> FeatureDefinitions
            {
                get
                {
                    if (_featureDefinitions == null)
                    {
                        _featureDefinitions = new ObservableCollection<FeatureDefinitionContainer>();
                        FillCollectionAsync(_featureDefinitions, delegate()
                        {
                            return Scopes.SelectMany(sc => sc.FeatureDefinitions)
                                .OrderBy(fdc => fdc.FeatureDefinition.DisplayName);
                        });
                    }
                    return _featureDefinitions;
                }
            }

            private ObservableCollection<ScopeContainer> _scopes;
            public ObservableCollection<ScopeContainer> Scopes
            {
                get
                {
                    if (_scopes == null)
                    {
                        _scopes = new ObservableCollection<ScopeContainer>();
                        FillCollectionAsync(_scopes, delegate()
                        {
                            return Solution.FeatureDefinitions.GroupBy(fd => fd.Scope)
                                .OrderBy(g => g.Key)
                                .Select(g => BuildScopeContainer(g));
                        });
                    }
                    return _scopes;
                }
            }

            private ScopeContainer BuildScopeContainer(IGrouping<SPFeatureScope, FeatureDefinition> g)
            {
                var sc = new ScopeContainer
                {
                    Parent = this,
                    DisplayName = GetScopeTitle(g.Key)
                };

                sc.FeatureDefinitions = g.Select(fd => new FeatureDefinitionContainer
                {
                    Parent = sc,
                    FeatureDefinition = fd
                }).ToList();

                return sc;
            }

            private string GetScopeTitle(SPFeatureScope scope)
            {
                switch (scope)
                {
                    case SPFeatureScope.Farm:
                        return "Farm";
                    case SPFeatureScope.Site:
                        return "Site";
                    case SPFeatureScope.Web:
                        return "Web";
                    case SPFeatureScope.WebApplication:
                        return "Web Application";
                }

                return "Unknown";
            }
        }

        public class ScopeContainer : SolutionsViewComponent<SolutionContainer>
        {
            public override TreeViewLevels Level { get { return TreeViewLevels.Scope; } }

            public override string DetailHeaderText { get { return "Solution Contents"; } }

            public string DisplayName { get; set; }
            public List<FeatureDefinitionContainer> FeatureDefinitions { get; set; }
        }

        public class FeatureDefinitionContainer : SolutionsViewComponent<ScopeContainer>
        {
            public override TreeViewLevels Level { get { return TreeViewLevels.FeatureDefinition; } }

            public override string DetailHeaderText { get { return "Deployed Features"; } }

            public FeatureDefinition FeatureDefinition { get; set; }

            private ObservableCollection<FeatureInstance> _activatedFeatures;
            public ObservableCollection<FeatureInstance> ActivatedFeatures
            {
                get
                {
                    if (_activatedFeatures == null)
                    {
                        _activatedFeatures = new ObservableCollection<FeatureInstance>();
                        FillCollectionAsync(_activatedFeatures, delegate()
                        {
                            return GetFeatureContainers(FeatureDefinition)
                                .SelectMany(fc => fc.Features)
                                .Where(f => f.Definition == FeatureDefinition)
                                .Select(f => new FeatureInstance(f));
                        });
                    }
                    return _activatedFeatures;
                }
            }

            private IEnumerable<IFeatureContainer> GetFeatureContainers(FeatureDefinition fd)
            {
                var farm = fd.Solution.Farm;
                if (fd.Scope == SPFeatureScope.Farm)
                    return new Farm[] { farm };

                var webApplications = farm.WebApplications;
                if (fd.Scope == SPFeatureScope.WebApplication)
                    return Cast(webApplications);

                var sites = webApplications.SelectMany(wa => wa.Sites);
                if (fd.Scope == SPFeatureScope.Site)
                    return Cast(sites);

                return Cast(sites.SelectMany(s => s.Webs));
            }

            private IEnumerable<IFeatureContainer> Cast(IEnumerable featureContainers)
            {
                return featureContainers.Cast<IFeatureContainer>();
            }
        }

        public class FeatureInstance
        {
            public FeatureInstance(IFeature feature)
            {
                Parent = GetParentDisplayName(feature.Parent);
                Activated = feature.Activated;
            }

            public string Parent { get; set; }
            public bool Activated { get; set; }

            private string GetParentDisplayName(IFeatureContainer parent)
            {
                var webApp = parent as WebApplication;
                if (webApp != null)
                    return webApp.Url;

                var site = parent as Site;
                if (site != null)
                    return site.Url;
                
                var web = parent as Web;
                if (web != null)
                    return web.Url;

                return parent.DisplayName;
            }
        }
    }
}
